package main

import (
	"flag"
	"strings"

	//"os"
	"fmt"
	"sync"
	"time"

	"github.com/panjf2000/gnet/v2"
	log "github.com/sirupsen/logrus"

	"cybertwin/ue"
	"cybertwin/utils/nettools"
	"cybertwin/utils/protocol"
	"cybertwin/utils/timer"
)

const (
	Rg_URL   = "localhost:8080"
	username = "王五"
)

var (
	apClient       ue.ApClient
	ctClient       ue.CybertwinClient
	directClient   ue.DirectClient
	registryClient ue.RegistryClient
	timer_         timer.Timer
	tmp, _         = nettools.GetMacAddr("10.176.40.190")
	clean          = strings.ReplaceAll(tmp, "-", ":")
	macAddr        = strings.ToUpper(clean)
)

func runRegistryClient(username string, macAddr string) (err error) {
	wg := &sync.WaitGroup{}
	wg.Add(1)
	registryClient = ue.RegistryClient{
		Username: username,
		MacAddr:  macAddr,
		Wg:       wg,
	}
	cli, err := gnet.NewClient(&registryClient)
	if err != nil {
		log.Errorln("runRegistryClient:", err.Error())
		return
	}

	_ = cli.Start()
	_, err = cli.Dial("tcp", Rg_URL)
	if err != nil {
		log.Errorln("runRegistryClient:", err.Error())
		return
	}

	wg.Wait()
	cli.Stop()
	return err
}

func runAPClient() (err error) {
	//dir, err := os.Getwd()
	timer_.Begin()

	wg := &sync.WaitGroup{}
	wg.Add(1)

	apClient = ue.ApClient{
		Ueid: registryClient.Ueid,
		Hid:  registryClient.Hid,
		Wg:   wg,
	}
	cli, err := gnet.NewClient(&apClient)
	if err != nil {
		log.Errorln("runAPClient:", err.Error())
		return
	}
	_ = cli.Start()
	_, err = cli.Dial("tcp", fmt.Sprintf("%s:%s", registryClient.Ap_IP, registryClient.Ap_port))
	if err != nil {
		log.Errorln("runAPClient:", err.Error())
		return
	}

	wg.Wait()
	cli.Stop()

	return
}

// runCyberTwinClient 入参t在正常情况下传入nil即可。
func runCyberTwinClient(t *timer.Timer) (conn gnet.Conn, cli *gnet.Client, err error) {

	wg := &sync.WaitGroup{}
	wg.Add(1)

	ctClient = ue.CybertwinClient{
		Ueid:              apClient.Ueid,
		R3:                apClient.R3,
		Token:             apClient.Token,
		CanRequest:        false,
		Wg:                wg,
		TimerForReconnect: t,
	}
	cli, err = gnet.NewClient(&ctClient)
	if err != nil {
		log.Errorln("runCyberTwinClient:", err.Error())
		return
	}
	err = cli.Start()
	if err != nil {
		return
	}
	// IP地址和端口号不应该是写死的，应该是AP告知的
	conn, err = cli.Dial("tcp", fmt.Sprintf("%s:%s", apClient.Ct_ip, apClient.Ct_port))
	if err != nil {
		log.Errorln("runCyberTwinClient:", err.Error())
		return
	}

	wg.Wait()

	dura := timer_.Stop()
	log.Infof("Access Time Cost:%v", dura)

	return
}

func makeRequest(conn gnet.Conn, requestid uint32, state byte) (err error) {
	ctClient.Req_map[requestid] = &timer.Timer{}
	ctClient.Req_map[requestid].Begin()
	echo_message := []byte("Hello, Cybertwin!")
	reqHeader := protocol.Request_Header{
		ProviderID:  uint32(1 << 30),
		ServiceID:   2,
		RequestID:   requestid,
		Token:       0, //ctClient.Token, todo: token传入大小，而不是token本身
		DataSize:    uint32(len(echo_message)),
		Dscp:        7,
		State:       state,
		MaxRespTime: 5,
	}
	body, _ := protocol.CodecUtils.RequestProtocolCodec.AppendHeader(reqHeader, echo_message)
	data, _ := protocol.CodecUtils.AccessProtocolCodec.AESEncode(body, protocol.TYPE_UE_CYBERTWIN_REQUEST, ctClient.Ck3)
	_, err = conn.Write(data)
	if err != nil {
		return
	}
	log.Infof("[UE] UE client has sent a request, requestid = %d\n", requestid)
	return
}

var runMode = flag.String("mode", "normal", "-mode [normal|fail]")

func normalCase() {
	err := runRegistryClient(username, macAddr)
	if err != nil {
		log.Errorln("run RegistryClient  failed", err.Error())
		return
	}
	err = runAPClient()
	if err != nil {
		log.Errorln(err.Error())
		return
	}
	conn, _, err := runCyberTwinClient(nil)
	if err != nil {
		log.Errorln("run Cybertwin Client failed")
		return
	}
	log.Infof("UE Client is ready to make request\n")
	for i := 1; i < 2; i++ {
		if ctClient.CanRequest {
			// 发请求
			err := makeRequest(conn, uint32(i), 1)
			if err != nil {
				log.Errorln("make request failed:", err.Error())
			}
		}
		time.Sleep(time.Millisecond * 500)
	}
	wg := &sync.WaitGroup{}
	wg.Add(1)
	wg.Wait()
}

// failCase cybertwin断线重连效果运行。
// 这个方法里面的time.Sleep比较关键，从测试的角度来看，sleep的时间需要随着echo server处理时间的不同而不同
// 否则可能无法的产生ct向ue转发resp的时候，ue恰好close connection这种情况
func failCase() {
	err := runRegistryClient(username, macAddr)
	if err != nil {
		log.Errorln("run RegistryClient  failed", err.Error())
		return
	}
	err = runAPClient()
	if err != nil {
		log.Errorln(err.Error())
		return
	}
	t := &timer.Timer{}
	t.Begin()
	conn, _, err := runCyberTwinClient(t)
	if err != nil {
		log.Errorln("run Cybertwin Client failed")
		return
	}

	log.Infoln("UE Client is ready to make request")
	requestId := 1

	// 发请求
	err = makeRequest(conn, uint32(requestId), 1)
	if err != nil {
		log.Errorln("make request failed: ", err.Error())
	}
	time.Sleep(time.Millisecond * 100)
	err = conn.Close()
	if err != nil {
		log.Errorln("close conn failed: ", err.Error())
	}
	time.Sleep(time.Second * 2) // 尽可能延长重连时间

	// 重连
	conn, _, err = runCyberTwinClient(t)
	if err != nil {
		log.Errorln("run Cybertwin Client failed")
		return
	}
	log.Infoln("UE Client is ready to make request")

	// 发请求
	err = makeRequest(conn, uint32(requestId), 1)
	if err != nil {
		return
	}
	time.Sleep(time.Millisecond * 1000)
}

func runDirectClient(t *timer.Timer) (conn gnet.Conn, err error) {
	directClient = ue.DirectClient{
		Wg:                &sync.WaitGroup{},
		TimerForReconnect: t,
	}
	cli, err := gnet.NewClient(&directClient)
	if err != nil {
		log.Errorf("[makeDirectRequest]-创建directClient失败:%s", err.Error())
		return
	}
	err = cli.Start()
	if err != nil {
		log.Errorf("[makeDirectRequest]-启动direClient失败:%s", err.Error())
		return
	}
	conn, err = cli.Dial("tcp", "localhost:9000")
	if err != nil {
		log.Errorf("[makeDirectRequest]-direClient连接echo server失败:%s", err.Error())
		return
	}
	return
}

// makeDirectRequest 直接向echo server发送请求
func makeDirectRequest(conn gnet.Conn) {
	conn.Write([]byte("Hello, echo server!"))
}

func standardRequest() {
	// 只运行一次请求，代表断线重连的那一次请求
	t := &timer.Timer{}
	t.Begin()
	conn, err := runDirectClient(t)
	if err != nil {
		log.Errorln("run Cybertwin Client failed")
		return
	}

	log.Infoln("UE Client is ready to make request")

	makeDirectRequest(conn)
	time.Sleep(time.Millisecond * 100)
	err = conn.Close()
	if err != nil {
		log.Errorln("close conn failed: ", err.Error())
	}
	time.Sleep(time.Second * 2) // 尽可能延长重连时间

	// 重连
	conn, err = runDirectClient(t)
	if err != nil {
		log.Errorln("run Cybertwin Client failed")
		return
	}
	log.Infoln("UE Client is ready to make request")
	makeDirectRequest(conn)
	time.Sleep(time.Millisecond * 1200)
}

func main() {
	flag.Parse()
	switch *runMode {
	case "normal":
		log.Info("[main]-normal case")
		normalCase()
	case "fail":
		failCase()
	case "compare":
		standardRequest() // 运行直接连接请求耗时
		failCase()        // 运行ct重连的请求耗时
	default:
		log.Errorln("[main]-命令行使用方式错误")
		flag.PrintDefaults()
	}

}
